﻿using System;
using System.Linq.Expressions;
using IslandUnit.Providers;

namespace IslandUnit
{
    /// <summary>
    /// Contains possible setups to stubs created to the dependency.
    /// </summary>
    /// <typeparam name="TDependency">The type of the dependency.</typeparam>
    /// <typeparam name="TResult">The type of object returned.</typeparam>
    public class StubItemSetup<TDependency, TResult> : IHideObjectMembers where TDependency : class
    {
        private readonly Dependency<TDependency> _dependency;
        private readonly Expression<Func<TDependency, TResult>> _method;

        /// <summary>
        /// Initializes a new instance of the <see cref="StubItemSetup&lt;TDependency, TResult&gt;"/> class.
        /// </summary>
        /// <param name="dependency">The <see cref="Dependency&lt;TDependency&gt;"/> of your dependency.</param>
        /// <param name="method">The target function of the stub.</param>
        public StubItemSetup(Dependency<TDependency> dependency, Expression<Func<TDependency, TResult>> method)
        {
            this._dependency = dependency;
            this._method = method;
        }

        /// <summary>
        /// Setup the stub to return one item of type TResult"/>.
        /// The item will be created using the builder framework setted up.
        /// </summary>
        /// <returns>An <see cref="StubItemAction&lt;TDependency, TResult&gt;"/>, where you can define other stub behaviors.</returns>
        public StubItemAction<TDependency, TResult> ReturningOne()
        {
            var item = ProviderRepository.Instance().BuilderProvider.CreateOne<TResult>();

            ProviderRepository.Instance().MockProvider.MockReturn(this._dependency.Mock, this._method, item);

            return new StubItemAction<TDependency, TResult>(this._dependency, item);
        }

        /// <summary>
        /// Setup the stub to return one item of type TResult"/>.
        /// </summary>
        /// <typeparam name="TDependency">The type of the dependency.</typeparam>
        /// <param name="obj">The item to be returned by the setub.</param>
        /// <returns>An <see cref="StubItemAction&lt;TDependency, TResult&gt;"/>, where you can define other stub behaviors.</returns>
        public StubItemAction<TDependency, TResult> Returning(TResult obj)
        {
            ProviderRepository.Instance().MockProvider.MockReturn(this._dependency.Mock, this._method, obj);

            return new StubItemAction<TDependency, TResult>(this._dependency, obj);
        }

        /// <summary>
        /// Setup the stub to return Throw an exception when call a method"/>.
        /// </summary>
        /// <typeparam name="TDependency">The type of the dependency.</typeparam>
        /// <typeparam name="TException">The type of exception to throw.</typeparam>
        /// <returns>An <see cref="StubItemAction&lt;TDependency, TResult&gt;"/>, where you can define other stub behaviors.</returns>
        public StubItemAction<TDependency, TResult> ThrowingException<TException>() where TException : Exception, new()
        {
            ProviderRepository.Instance().MockProvider.MockThrowException<TDependency, TResult, TException>(this._dependency.Mock, this._method);

            return new StubItemAction<TDependency, TResult>(this._dependency, default(TResult));
        }
    }
}
